from django.db import models
from django.contrib.auth.models import User
from . import CHANGE_TYPES, log

# FIXME: need thorough testing to determine if all fields and options that are
#        currently disallowed are enough to keep history running smooth.
# FIXME: related fields are currently not added to corresponding history object
#        these should be accounted for by adding a PositiveIntegerField in its
#        place

def create_model(name, fields=None, app_label='', module='', options=None, admin=None):
    """
        Creates and returns a django model class with given fields, app_label,
        module name, and meta options. Taken from:
            http://code.djangoproject.com/wiki/DynamicModels
    """
    class Meta:
        # Using type('Meta', ...) gives a dictproxy error during model creation
        pass

    if app_label:
        # app_label must be set using the Meta inner class
        setattr(Meta, 'app_label', app_label)

    # Update Meta with any options that were provided
    if options is not None:
        for key, value in options.items():
            setattr(Meta, key, value)

    # Set up a dictionary to simulate declarations within a class
    attrs = {'__module__': module, 'Meta': Meta}

    # Add in any fields that were provided
    if fields:
        attrs.update(fields)

    # Create an Admin inner class if admin options were provided
    if admin is not None:
        class Admin:
            pass
        for key, value in admin:
            setattr(Admin, key, value)
        attrs['Admin'] = Admin

    # Create the class, which automatically triggers ModelBase processing
    return type(name, (models.Model,), attrs)


# Invalid field settings for history models
invalid_field_settings = (
    'creation_counter',
    'primary_key',
    'unique',
    'unique_for_date',
    'unique_for_month',
    'unique_for_year',
    'rel',
    'attname',
    'db_column',
    'db_index',
    'column',
)

def filter_field_settings(settings):
    """
        Removes invalid field settings from the given fields dict. and returns
        sanitized copy.
    """
    
    filtered = {}
    for k, v in settings.items():
        if k == '_choices':
            filtered['choices'] = v
        elif k not in invalid_field_settings:
            filtered[k] = v
    return filtered


def clean_fields(model_class):
    """
        Redefines unacceptable field types (relatedfields, autofields) and
        returns a dictionary of acceptable fields and settings derived from the
        given model.
    """

    fields = [field for field in model_class._meta.fields]
    cleaned_fields = list()
    for field in fields:
        name = field.attname
        while isinstance(field, models.fields.related.RelatedField):
            field = field.rel.to._meta.pk
        if isinstance(field, models.fields.AutoField):
            new_field = models.fields.PositiveIntegerField()
            new_field.name = name
        else:
            # should copy field dict and set 'name' key to local name?
            new_field = field.__class__(**filter_field_settings(field.__dict__))
        cleaned_fields.append((new_field.name, new_field))
    return dict(cleaned_fields)


def create_history_model(model_class):
    """
        Creates and returns a history model with fields corresponding to those
        in the given object. Also adds history metadata fields.
    """

    fields = clean_fields(model_class)

    fields['hst_modified_from_ip'] = models.IPAddressField(default='0.0.0.0', verbose_name=u'Modified from IP')
    fields['hst_revision']         = models.IntegerField(default=-1, editable=False, verbose_name=u'Revision number')
    fields['hst_modified_on']      = models.DateTimeField(auto_now=True, verbose_name=u'Time changed')
    fields['hst_modified_by_user'] = models.PositiveIntegerField(null=True, blank=True, verbose_name=u'ID of user that modified')
    fields['hst_revision_type']    = models.CharField(maxlength=1, choices=CHANGE_TYPES)
    fields['hst_record_id']        = models.AutoField(primary_key=True)

    # Have django enforce revision numbers
    options = {
        'unique_together': ((model_class._meta.pk.attname, 'hst_revision'),),
    }

    # model's db_table is app_label + module

    return create_model(
        '%sHistory' % model_class.__name__,
        fields=fields,
        app_label=model_class._meta.app_label,
        module=model_class.__module__ + '_history',
        options=options,
        admin={},
    )

